Protocolo ECCP v0.1
-------------------
Revisión 4
Estatus: Alfa

Objetivo
--------
El protocolo ECCP es un protocolo basado en XML cuya finalidad es la de proveer 
de un API de comunicaciones a través de un puerto TCP para que aplicaciones externas
puedan comunicarse con el motor de call center que provee Elastix.

La carencia de un mecanismo de comunicación entre el motor de call center y las 
aplicaciones cliente, que permita la transmisión de eventos asincrónicos, fue una 
de las motivaciones para la creación del ECCP. Antes del ECCP una aplicación cliente 
debía enviar peticiones periódicas al servidor para enterarse de la ocurrencia de 
eventos asincrónicos.

Por otro lado, era necesario una estandarización del protocolo de comunicaciones
con el motor de call center con miras a permitir escalabilidad y organización.

Convenciones usadas en este documento
-------------------------------------
En la mayor parte de este documento, cuando se refiera a “atributos” y “elementos”, 
estos términos estarán relacionados con su definición dentro del lenguaje XML.

Cuando se mencione la palabra servidor (a secas) se estará haciendo referencia al 
servidor de call center, que viene junto con el módulo de call center de Elastix. 
Este programa también recibe el nombre de “dialerd”.

Toda fecha tendrá formato yyyy-mm-dd y todo hora tendrá formato hh:mm:ss. La zona
horaria con la que se opera es siempre la que se ha configurado en el servidor
que ejecuta el programa dialerd. 

La palabra 'CallCenter' (sensitiva a mayusculas) se utilizará para referirse
al módulo CallCenter de Elastix.

Arquitectura
------------

-------------      -----     ---------------------------
|           |     /     \    |                         |
| Servidor  |-----  Red  ----| Cliente                 |
| (dialerd) |     \     /    | (Ej. Consola de agente) |
|           |      -----     |                         |
-------------                ---------------------------

Comunicaciones TCP/IP
---------------------

La comunicación será TCP en el puerto 20005.

Sesiones
--------
El protocolo ECCP está orientado a sesiones, teniendo la capacidad de soportar 
múltiples sesiones de comunicación, puediendo atender de esta manera a varias 
aplicaciones cliente a la vez.

Una sesión quedará establecida luego de que la aplicación cliente se haya 
autenticado correctamente. La sesión permanecerá activa hasta que la aplicación
cliente termine la sesión o hasta que ocurra un timeout por inactividad.

El valor del timeout por inactividad será de 5 minutos, luego de lo cual el 
servidor terminará la sesión.

Separación entre cliente y agente
---------------------------------
El servidor se comunica con apliaciones cliente y es la aplicación cliente la 
que gestiona el ingreso de los agentes al sistema. Una aplicación cliente puede
manejar a uno o más agentes.

Esto último abre las puertas a dos escenarios que se explican a continuación.

Escenario 1: Consolas de agente que se conectan directamente al servidor.

Aquí cada consola de agente se encarga de gestionar el ingreso de un solo agente.
Para el servidor cada consola de agente es vista como una aplicación cliente.

                         --------------------------------
                /--------| Consola de agente (agente 1) |
               /         --------------------------------
------------  /          --------------------------------
| Servidor |------------ | Consola de agente (agente 2) |
------------  \          --------------------------------
               \         --------------------------------
                \--------| Consola de agente (agente 3) |
                         --------------------------------

Como vemos en el ejemplo anterior el servidor ha iniciado 3 sesiones, una para 
cada aplicación cliente, que en este caso vendría a ser cada consola de agente.
Este esquema es el más sencillo de implementar.

Escenario 2: Consolas de agente que se conectan mediante una aplicación cliente
intermediaria.

Aquí la aplicación cliente intermediaria se encarga de gestionar el ingreso de 
varios agentes. En este escenario, desde el punto de vista del servidor, la
aplicación cliente intermediaria es vista como una aplicación cliente, mientras
que las consolas de agente no.

                                                --------------------------------
                                        /-------| Consola de agente (agente 1) |
                                       /        --------------------------------
-----------   ----------------------  /         --------------------------------
| Servidor |--| App. intermediaria |----------- | Consola de agente (agente 2) |
-----------   ----------------------  \         --------------------------------
                                       \        --------------------------------
                                        \-------| Consola de agente (agente 3) |
                                                --------------------------------

Como vemos en el ejemplo anterior, el servidor ha iniciado UNA SOLA SESION en 
lugar de tres. 

En un futuro nos referirnos a esta aplicación intermediara como *servidor de 
concurrencia*. Un servidor de concurrencia es útil en varios sentidos. Uno de
ellos es que se liberan recursos del servidor Elastix, pues el servidor de 
concurrencia puede estar instalado en un equipo remoto. El servidor Elastix ya 
no tiene que encargarse de gestionar las sesiones para las aplicaciones 'cliente'.

El servidor de concurrencia también puede servir de servidor de caché para 
descongestionar los requerimientos al servidor Elastix. Por ejemplo, podría hacer 
caché de información de las campañas. 

Consideraciones de seguridad
----------------------------
Para mantener cierto control sobre las aplicaciones cliente que se conectan al 
servidor, este último mantendrá un registro de IPs autorizadas para conectarse.
En caso de que un cliente intente conectarse desde una dirección IP no 
autorizada, el servidor simplemente cerrará la conección con un mensaje 
“Connection refuesed” y escribirá un mensaje de error en el log respectivo.

Tipos de paquetes
-----------------
Existen tres tipos de paquetes en el protocolo ECCP:
* evento
* requerimiento
* respuesta

Cada paquete se representa como un documento XML bien formado, cuyas etiquetas
son definidas a continuación. 

Evento
------
Un evento es generado por un acontecimiento asincrónico ocurrido en el Servidor.
A continuación su sintáxis básica:
<event>
...
</event>

IMPORTANTE: para extensión futura del protocolo, pueden definirse eventos 
adicionales a los descritos en este documento, así como etiquetas y atributos
adicionales en eventos definidos. Un cliente de ECCP DEBE IGNORAR todo evento 
que no sepa manejar. De la misma manera, el cliente DEBE IGNORAR cualquier 
etiqueta o atributo que no sepa manejar en el evento que recibe.

Requerimiento
-------------
Un requerimiento es un mensaje enviado por la aplicación cliente hacia el 
servidor. El servidor contestará este mensaje con la respuesta apropiada.

En el requerimiento se incluye un atributo llamado id. Este id no es otra cosa
que una cadena de caracteres que sirve para poder identificar la respuesta. De 
este modo se minimiza la posibilidad de que ocurran problemas ocasionados por
condiciones de carrera. El identificador consta de dos partes unidas por un 
punto: la primera parte está constituida por el timestamp UNIX y la segunda 
parte está compuesta de un entero aleatorio de 6 caracteres.
Un ejemplo de identificador es el siguiente:

1292899827.394772

A continuación de un ejemplo de un requerimiento:

<request id="1292899827.394772">
...
</request>

Respuesta
---------
Un requerimiento puede producir una o varias respuestas. Las respuestas se 
relacionan con los requerimientos a través del identificador del requerimiento.
A continuación su sintaxis básica:

<response id=identificador>
...
</response>

IMPORTANTE: para extensión futura del protocolo, pueden definirse etiquetas y 
atributos adicionales en respuestas definidass. El cliente ECCP DEBE IGNORAR 
cualquier etiqueta o atributo que no sepa manejar en la respuesta que recibe.

Autenticación de Requerimientos
-------------------------------
Algunos de los Requerimientos listados en este manual ordenan actualizaciones en 
el estado del sistema que deben ser autorizadas únicamente para un agente en 
particular. Cuando un Requerimiento requiere ser autenticado, el método usado es 
el siguiente: 
* Cada agente tiene asignada una contraseña conocida por el servidor.
* El requerimiento "login" de la aplicación, en caso de tener éxito, devuelve
  un texto aleatorio como contenido del tag "app_cookie". De aquí en adelante
  a este texto se lo llamará cookie.
* Todo requerimiento que deba ser autenticado debe tener dos argumentos 
  obligatorios, agent_number y agent_hash .
* El parámetro agent_number es el nombre del agente, como "Agent/9000"
* El parámetro agent_hash es un valor resultante de aplicar el hash MD5 a la
  concatenación del texto cookie, el nombre del agente, y la contraseña del
  agente, en este orden. Es decir, para el agente Agent/9000 con contraseña
  de valor "unaclavesecreta":
  
  s = cookie + "Agent/9000" + "unaclavesecreta" 
  agent_hash <-- MD5(s)
  
  ...donde cookie contiene el valor devuelto en app_cookie por el login, y 
  el signo más (+) indica la operación de concatenación de cadenas.

Todo requerimiento que haga referencia a un argumento agent_hash debe de recibir
el valor de hash calculado de la manera indicada anteriormente.

Mensajes de error
-----------------
Los requerimientos definidos en este documento pueden reportar errores de la 
siguiente manera:

<response id=identificador>
  <failure>
    <code>XXX</code>
    <message>Algun mensaje</message>
  </failure>
</response>

o de la siguiente manera:

<response id=identificador>
  <request_response>
      <failure>
        <code>XXX</code>
        <message>Algun mensaje</message>
      </failure>
  </request_response>
</response>

La diferencia entre los dos métodos es que el primero indica errores de formato
o de protocolo que ocurren antes del procesamiento específico del requerimiento,
mientras que el segundo método indica un error dentro del procesamiento
específico del requerimiento.

Un requerimiento se considera fallido si contiene la etiqueta <failure>. Dentro
de la etiqueta se almacena un código de error y un mensaje descriptivo. El 
código de error está vagamente basado en los códigos de error de HTTP, pero no
se debe de contar con esta relación.

Los códigos de error más comunes son los siguientes:
Código de error     Significado
400                 Petición mal formada
401                 No autorizado
404                 No se encuentra el objeto
417                 Una condición esperada falla en cumplirse
500                 Error interno del programa
501                 No implementado

Casos comunes de errores
------------------------

Respuestas a requerimientos sin previo inicio de sesión:

En caso de requerimientos sin previo inicio de sesión, el servidor responderá así:

<response id=identificador>
  <failure>
    <code>401</code>
    <message>Unauthorized</message>
  </failure>
</response>

Respuestas a requerimientos mal formateados:

En caso de que el servidor no pueda entender algún requerimiento, responderá así:

<response>
  <failure>
    <code>400</code>
    <message>Bad request</message>
  </failure>
</response>

Como se puede observar, el atributo id no se incluye.

Modelo de funcionamiento de CallCenter
--------------------------------------
A continuación se describe el modelo de funcionamiento del sistema CallCenter.
Esta explicación proporciona una referencia para comprender los tipos de 
requerimientos que son necesarios en el protocolo ECCP, así como los parámetros
necesarios y opcionales en cada protocolo.

La asignación de llamadas en el CallCenter se implementa a través de las colas
de Asterisk. Cada cola se identifica con un número, y tiene dos tipos de 
actores: agentes y llamadas. Un agente es una extensión de Asterisk que se ha
registrado como perteneciente a la cola, para recibir llamadas. Una llamada
puede, a través de órdenes dadas al Asterisk vía AMI (Asterisk Manager Interface),
o vía un marcado directo a la central Asterisk, entrar a una cola. Al entrar,
oirá música de espera hasta que un agente se libere, en cuyo caso la llamada
será enlazada con este agente.

Los agentes pueden ser de tipo dinámico o estático. Los agentes dinámicos pueden
asociarse arbitrariamente a una cola sin configuración previa de Asterisk. Los
agentes estáticos se asocian como parte de la definición de la cola. Actualmente
el CallCenter sólo puede usar agentes estáticos. Un agente estático tiene un 
número asociado, que es independiente de la extensión telefónica usada para
hacer uso de este agente. Por ejemplo, una cola puede tener un agente 9000, el
cual es usado por la extensión 1064. Internamente en Asterisk, y por el resto
del documento, un agente se lo identifica con la cadena de canal: Agent/9000.
Un mismo agente puede pertenecer a múltiples colas.

Para que una extensión haga uso de un agente estático, debe de completar el 
procedimiento de 'login' a la cola. En Elastix, se ha configurado el número *8888
como login de cola arbitraria. Al marcar este número, la extensión oye una 
grabación para ingresar el número de agente que se va a usar. Luego de digitado,
se reproduce otra grabación para que ingrese la contraseña del agente. Si se
digita correctamente la contraseña, la extensión ingresa a la cola como un 
agente, y la extensión reproduce música de espera hasta que se le asigne una
llamada. Para finalizar el login a la cola, se cuelga la extensión, o se ejecuta
el método Agentlogoff del AMI indicando el canal Agent/XXXX que desea terminar
la sesión. Al realizar login y logout a través del CallCenter, el sistema lleva 
la auditoría de cuánto tiempo ha estado logoneado cada agente.

El CallCenter soporta dos modos principales de operación: campañas salientes y
campañas entrantes. Una campaña tiene asociada una cola, a través de la cual
se procesarán las llamadas asociadas a esta campaña. Tanto la campaña saliente,
como la campaña entrante, pueden estar en funcionamiento simultáneo en la misma
central, siempre que se asignen colas separadas para cada función. Para una
campaña entrante, se configura la central y los planes de marcado para que una
llamada que viene del exterior termine en una cola que ha sido registrada como
reservada para campañas entrantes (Interfaz Web: Call Center-->Ingoing Calls
-->Queues). Para una campaña saliente, se debe configurar una nueva campaña
(Call Center-->Outgoing Calls-->Campaigns) con una lista de números telefónicos
a marcar. El sistema entonces marcará cada uno de los números, asociando como
extensión a la cola donde esperan los agentes.

El sistema, luego de aceptar el login de los agentes, empieza a marcar o esperar
llamadas. Para cada llamada entrante, se crea un nuevo registro de la llamada
en la base de datos. Para las llamadas salientes, se actualiza el tiempo de 
duración de la llamada en la base de datos de los teléfonos cargados. El sistema
notifica al agente cuando una llamada ha sido asignada por Asterisk, y le 
transmite el ID de la campaña, el ID de la llamada asociada, los atributos de la
llamada que hayan sido cargados en la base de datos, el texto asociado que debe
recitar el agente (el llamado "script" de la campaña), y los IDs de los 
formularios que se pueden usar para recoger datos sobre la llamada. La interfaz
del lado del cliente recoge los datos para llenar el formulario, y se los 
transmite al sistema CallCenter para ser almacenados. (NOTA: la intefaz antigua
del CallCenter no tiene todavía soporte para asociar formularios a una campaña
entrante)

Para el caso de las llamadas entrantes, existe una lista de contactos identificada
por número telefónico. El sistema identifica el número vía el servicio de 
Caller-ID e intenta mostrar los atributos del contacto que corresponde a una
llamada entrante. Sin embargo, puede ocurrir que existan múltiples contactos que
tienen el mismo número telefónico asociado. En este caso el CallCenter transmite
la información de todos los contactos que coinciden con el número telefónico, y
el agente humano debe de confirmar cuál contacto fue el que se ha recibido.

El sistema CallCenter permite que un agente pause la recepción de llamadas como
parte de un receso controlado, también controlado y auditado. A estos recesos
se los llama "breaks". Cuando un agente debe ingresar a un break, se selecciona
desde la interfaz el tipo de break que debe de usar. Luego, al terminar el break
el agente lo indica a través de la interfaz, y empieza a recibir llamadas otra
vez.

Listado de eventos
------------------

Evento "agentlinked"

Este evento se genera cuando un agente ha sido enlazado con una llamada ingresada
a la cola. Internamente se ha recibido el evento Link/Bridge para la llamada.

Elementos informativos:
* agent_number: Agente que ha sido enlazado con la llamada. Este identificador 
  está de la forma 'Agent/9000'.
* remote_channel: Canal que representa la llamada que ha sido enlazada. Puede
  estar en una variedad de formatos, pero el más frecuente es PROTOCOLO/XXX-YYYY.
  Por ejemplo: 'SIP/unatroncal-0000001'.
* call_type: Uno de los dos valores 'incoming' u 'outgoing', para indicar si la
  llamada es entrante o saliente, respectivamente.
* campaign_id: ID de la base de datos de la campaña, un entero. Este valor es
  requerido para identificar una llamada saliente. Para una llamada entrante,
  este valor es opcional, e incluso puede estar omitido, si la llamada entrante
  no está asociada a una campaña.
* call_id: ID de la base de datos de la llamada, un entero. Con este ID, y el 
  valor de call_type (y campaign_id para llamadas salientes), el código cliente
  puede identificar la llamada a procesar para formularios.
* phone: El número que fue marcado, o el Caller-ID que fue recibido.
* status: El estado de la llamada. Usualmente es 'Success' para llamada saliente
  y 'activa' para llamada entrante.
* uniqueid: ID interno de Asterisk que identifica a la llamada remota.
* datetime_originate: (Sólo llamada saliente) Fecha y hora en que se inició el
  marcado de la llamada.
* datetime_originateresponse: (Sólo llamada saliente) Fecha y hora en que se
  recibió respuesta del marcado de la llamada.
* datetime_join: Fecha y hora en que la llamada ingresó a la cola.
* datetime_linkstart: Fecha y hora en que la llamada fue enlazada al agente.
* retries: (Opcional) Número de veces que la llamada ha sido reintentada.
* trunk: (Sólo llamada entrante) Troncal a través de la cual se recibió la llamada.
  Por ejemplo: DAHDI/4 o SIP/unatroncal
* queue: (Sólo llamada entrante) Cola que recibió la llamada
* call_attributes: (Opcional) Atributos asociados a la llamada en la base de 
  datos. Esta es una lista que contiene múltiples elementos 'attribute'. Cada
  elemento attribute tiene los siguientes elementos:
  * label: Etiqueta de texto asociada al atributo
  * value: Valor de texto del atributo
  * order: Orden en que se sugiere que se muestren los atributos
* matching_contacts: (Opcional, sólo llamada entrante) Lista de contactos cuyo
  número de teléfono coincide con el Caller-ID de la llamada. Si hay más de un
  contacto, la interfaz debe usar el requerimiento setcontact para indicar cuál
  contacto debe asociarse con la llamada recibida. Este elemento es una lista
  que contiene múltiples elementos 'contact'. Cada elemento contact tiene un
  atributo 'id' que debe usarse en el requerimiento setcontact. Además cada 
  contacto tiene múltiples elementos 'attribute'. Cada elemento attribute
  contiene los siguientes elementos:
  * label: Etiqueta de texto asociada al atributo del contacto
  * value: Valor de texto del atributo del contacto
  * order: Orden en que se sugiere que se muestren los atributos del contacto
* call_survey: (Opcional) Lista de formularios con los datos recogidos para esta
  llamada. Este elemento sólo está presente si se ha recogido previamente datos
  para esta llamada. Este elemento es una lista que contiene múltiples elementos
  'form'. Cada elemento form tiene un atributo 'id' que debe usarse en el 
  requerimiento saveformdata. Además cada form tiene múltiples elementos 
  'field'. Cada elemento field tiene un atributo 'id' que pueden usarse también
  en el requerimiento saveformdata. El elemento field tiene ademas los 
  siguientes elementos:
  * label: Etiqueta del elemento de formulario
  * value: Valor que fue recogido en el formulario

NOTA: para averiguar los formularios aplicables a una llamada que no ha sido
previamente llenada, se debe usar el requerimiento getcampaigninfo.

Ejemplo:

Llamada entrante:
<event>
    <agentlinked>
        <agent_number>Agent/9000</agent_number>
        <remote_channel>SIP/1065-00000001</remote_channel>
        <calltype>incoming</calltype>
        <call_id>13</call_id>
        <campaign_id>1</campaign_id>
        <phone>1065</phone>
        <status>activa</status>
        <uniqueid>1296517374.1</uniqueid>
        <datetime_join>2011-01-31 18:42:55</datetime_join>
        <datetime_linkstart>2011-01-31 18:42:55</datetime_linkstart>
        <trunk>SIP/1065</trunk>
        <queue>8001</queue>
        <call_attributes>
            <attribute>
                <label>first_name</label>
                <value>Usuario</value>
                <order>1</order>
            </attribute>
            <attribute>
                <label>last_name</label>
                <value>Windows</value>
                <order>2</order>
            </attribute>
            <attribute>
                <label>phone</label>
                <value>1065</value>
                <order>3</order>
            </attribute>
            <attribute>
                <label>cedula_ruc</label>
                <value>0915172176</value>
                <order>4</order>
            </attribute>
        </call_attributes>
        <matching_contacts>
            <contact id="2">
                <attribute>
                    <label>first_name</label>
                    <value>Usuario</value>
                    <order>1</order>
                </attribute>
                <attribute>
                    <label>last_name</label>
                    <value>Windows</value>
                    <order>2</order>
                </attribute>
                <attribute>
                    <label>phone</label>
                    <value>1065</value>
                    <order>3</order>
                </attribute>
                <attribute>
                    <label>cedula_ruc</label>
                    <value>0915172176</value>
                    <order>4</order>
                </attribute>
            </contact>
        </matching_contacts>
        <call_survey/>
    </agentlinked>
</event>

Llamada saliente:
<event>
    <agentlinked>
        <agent_number>Agent/9000</agent_number>
        <remote_channel>Local/1065@from-internal-988d;1</remote_channel>
        <calltype>outgoing</calltype>
        <call_id>27</call_id>
        <campaign_id>5</campaign_id>
        <phone>1065</phone>
        <status>Success</status>
        <uniqueid>1296517619.3</uniqueid>
        <datetime_originate>2011-01-31 18:46:59</datetime_originate>
        <datetime_originateresponse>2011-01-31 18:47:07</datetime_originateresponse>
        <datetime_join>2011-01-31 18:47:07</datetime_join>
        <datetime_linkstart>2011-01-31 18:47:07</datetime_linkstart>
        <retries>1</retries>
        <call_attributes>
            <attribute>
                <label>Nombre</label>
                <value>Zoila</value>
                <order>1</order>
            </attribute>
            <attribute>
                <label>Apellido</label>
                <value>Chacha</value>
                <order>2</order>
            </attribute>
            <attribute>
                <label>Campo1</label>
                <value>gato</value>
                <order>3</order>
            </attribute>
            <attribute>
                <label>Campo2</label>
                <value>perro</value>
                <order>4</order>
            </attribute>
            <attribute>
                <label>Campo3</label>
                <value>mono</value>
                <order>5</order>
            </attribute>
        </call_attributes>
        <call_survey/>
    </agentlinked>
</event>

Evento "agentunlinked"

Este evento se genera cuando se deshace el enlace entre un agente y la llamada
previamente enlazada. Internamente se ha recibido el evento Hangup del lado
remoto.

Elementos informativos:
* agent_number: Agente que ha sido desenlazado de la llamada. Este identificador 
  está de la forma 'Agent/9000'.
* call_type: Uno de los dos valores 'incoming' u 'outgoing', para indicar si la
  llamada es entrante o saliente, respectivamente.
* campaign_id: (Opcional) ID de la base de datos de la campaña, un entero. Este
  valor es requerido para identificar una llamada saliente. Para una llamada 
  entrante, este valor es opcional, e incluso puede estar omitido, si la llamada
  entrante no está asociada a una campaña.
* call_id: ID de la base de datos de la llamada, un entero. Con este ID, y el 
  valor de call_type (y campaign_id para llamadas salientes), el código cliente
  puede identificar la llamada a procesar para formularios.
* phone: El número que fue marcado, o el Caller-ID que fue recibido.

Ejemplo:
<event>
    <agentunlinked>
        <agent_number>Agent/9000</agent_number>
        <calltype>incoming</calltype>
        <campaign_id>1</campaign_id>
        <call_id>13</call_id>
        <phone>1065</phone>
    </agentunlinked>
</event>

Evento "agentloggedin"

Este evento se genera cuando el agente indicado ha ingresado al sistema. La 
separación de esto como un evento permite que la respuesta al requerimiento 
loginagent regrese de inmediato mientras el agente se demora digitando la clave.

Nota: no debe de confundirse este evento con el requerimiento loginagent.

Elementos informativos:
* agent: Agente que ha podido logonearse. Este identificador está de la forma 
  'Agent/9000'.
* queues: Lista de colas a la que pertenece el agente. Este elemento contiene
  múltiples elementos 'queue', que contienen cada uno una cola a la que 
  pertenece el agente.

Ejemplo:
<event>
    <agentloggedin>
        <agent>Agent/9000</agent>
        <queues>
            <queue>8001</queue>
            <queue>8000</queue>
        </queues>
    </agentloggedin>
</event>

Evento "agentloggedout"

Este evento se genera cuando el agente indicado ha sido deslogoneado del sistema,
sea por voluntad del agente (requerimiento logoutagent), o porque la extensión
asociada ha sido colgada o se perdió la comunicación.

Nota: no debe de confundirse este evento con el requerimiento logoutagent.

Elementos informativos:
* agent: Agente que ha sido deslogoneado. Este identificador está de la forma 
  'Agent/9000'.
* queues: Lista de colas a la que pertenece el agente. Este elemento contiene
  múltiples elementos 'queue', que contienen cada uno una cola a la que 
  pertenece el agente.

Ejemplo:
<event>
    <agentloggedout>
        <agent>Agent/9000</agent>
        <queues>
            <queue>8001</queue>
            <queue>8000</queue>
        </queues>
    </agentloggedout>
</event>

Evento "agentfailedlogin"

Este evento se genera cuando el agente indicado ha intentado logonearse al 
sistema pero el intento ha fallado. Una posible causa es que el agente no haya
digitado la contraseña de ingreso a la cola.

Elementos informativos:
* agent: Agente que ha intentado logonearse. Este identificador está de la forma 
  'Agent/9000'.

Ejemplo:
<event>
    <agentfailedlogin>
        <agent>Agent/9000</agent>
    </agentfailedlogin>
</event>

Listado de requerimientos
-------------------------
Nota: Para facilitar la legibilidad del presente documento, se presentarán aquí 
también las "respuestas" a cada requerimiento.

Requerimiento "login"

Este requerimiento sirve para autenticar una aplicación cliente y permitirle 
establecer una sesión de comunicaciones con el servidor. El servidor ECCP no 
transmitira evento alguno hasta que el cliente haya completado un login correcto
con el servidor.

Atributos o argumentos:
* username: Nombre de usuario asignado al cliente
* password: Contraseña correspondiente al nombre de usuario

Respuesta:
* app_cookie: Cadena de texto arbitraria que se genera como parte del login.
  El cliente debe de almacenar esta cadena para poder realizar el requerimiento
  loginagent.

* Encriptación de la clave
La clave va "encriptada" con MD5. El hash se calcula en base a una semilla, la 
cual es un secreto compartido entre la aplicación cliente y el servidor. Es 
decir que la aplicación cliente previamente se tendrá que configurar para 
conocer dicho secreto.
TODO: secreto compartido no implementado todavía. Se requiere acordar manera de
usar semilla. Por ahora se acepta clave en plaintext, o md5 de la misma clave.

Ejemplo:
<request id="1">
    <login>
        <username>agentconsole</username>
        <password>agentconsole</password>
     </login>
</request>
Alternativamente:
<request id="1">
    <login>
        <username>agentconsole</username>
        <!-- El hash de abajo es md5 de la cadena "agentconsole" -->
        <password>7e53aa92f7566f44f39f3f1842832581</password>
    </login>
</request>

Respuesta:
En caso de éxito: 
<response id="1">
    <login_response>
        <success/>
        <app_cookie>35d290884ef77a78cc6c0006b7e1d576</app_cookie>
    </login_response>
</response>

En caso de usuario o clave inválida:
<response id="1">
    <login_response>
        <failure>
            <code>401</code>
            <message>Invalid username or password</message>
        </failure>
    </login_response>
</response>

Requerimiento "logout"

El requerimiento “logout” sirve para terminar la sesión entre la aplicación 
cliente y el servidor. Luego de ejecutar este requerimiento, el servidor cerrará
la sesión TCP con el cliente.

Atributos o argumentos:
Este requerimiento no tiene atributos.

Ejemplo:
<request id="1000">
  <logout></logout>
</request>

Respuesta:
<response id="1000">
    <logout_response>
        <success/>
    </logout_response>
</response>
(y conexión TCP se cierra)

Requerimiento "getagentstatus"

El requerimiento "getagentstatus" informa el estado actual de disponibilidad del
agente indicado. 

Atributos o argumentos:
* agent_number: Agente que se interroga. Este identificador está de la forma 
  'Agent/9000'.

Ejemplo:
<request id="4">
    <getagentstatus>
        <agent_number>Agent/9000</agent_number>
    </getagentstatus>
</request>

Respuesta:
En caso de que existe el agente:
<response id="4">
    <getagentstatus_response>
        <status>offline</status>
    </getagentstatus_response>
</response>

El elemento "status" tiene uno de los siguientes posibles valores:
* offline: Agente no ha iniciado sesión en la cola
* online: Agente ha iniciado sesión, y está ocioso
* oncall: Agente ha iniciado sesión, y está atendiendo una llamada
* paused: Agente ha iniciado sesión, y está pausado (en break)

En caso de que no exista el agente:
<response id="4">
    <getagentstatus_response>
        <status>offline</status>
        <failure>
            <code>417</code>
            <message>Invalid agent number</message>
        </failure>
    </getagentstatus_response>
</response>

Requerimiento "loginagent"

Este requerimiento inicia la conexión de una extensión, bajo la identidad de un
agente específico, hacia una cola. En la implementación actual, Asterisk inicia
una llamada hacia la extensión, la cual al ser contestada, reproduce una 
petición de contraseña. Al digitar la contraseña correctamente, el agente queda
logoneado a la cola, y se emite el evento "agentloggedin". Si no se contesta
la llamada, o si la contraseña se digita incorrectamente, se emite el evento
"agentfailedlogin". La respuesta a este requerimiento indica únicamente que la
llamada hacia el agente ha sido iniciada, no que el agente haya sido aceptado
en la cola

Atributos o argumentos:
* agent_number: El número de agente a logonear. Este identificador está de la 
  forma 'Agent/9000'.
* agent_hash: Hash de autenticación para el agente.
* extension: El número de extensión a ingresar bajo la identidad del agente.
* password: (opcional) Clave telefónica del agente (no se usa en la implementación actual)

Ejemplo:
<request id="2">
    <loginagent>
        <agent_number>Agent/9000</agent_number>
        <agent_hash>XXXXXXXXXXXXXXXXXXXXXXXXX</agent_hash>
        <extension>1064</extension>
    </loginagent>
</request>

Respuesta: 
Al marcarse la llamada e iniciar el proceso de login:
<response id="2">
    <loginagent_response>
        <status>logging</status>
    </loginagent_response>
</response>

Al ocurrir un error de extensión:
<response id="2">
    <loginagent_response>
        <status>logged-out</status>
        <failure>
            <code>417</code>
            <message>Invalid extension number</message>
        </failure>
    </loginagent_response>
</response>

El elemento "status" tiene uno de los siguientes posibles valores:
* logging: Llamada ha sido marcada y espera respuesta
* logged-in: Agente ya estaba anteriormente logoneado con ECCP en la misma extensión
* logged-out: No se puede iniciar el proceso de login. Posibles causas:
  * Agente indicado no existe
  * Extensión indicada no existe
  * Agente ya logoneado con un medio distinto al ECCP
  * Agente ya logoneado en una extensión distinta

Requerimiento "logoutagent"

Este requerimiento termina la sesión de un agente en las colas a la que 
pertenece. Luego de finalizada la sesión, se emite un evento "agentlogoff".

Atributos o argumentos:
* agent_number: El número de agente a logonear. Este identificador está de la 
  forma 'Agent/9000'.
* agent_hash: Hash de autenticación para el agente.

Ejemplo:
<request id="3">
    <logoutagent>
        <agent_number>Agent/9000</agent_number>
        <agent_hash>XXXXXXXXXXXXXXXXXXXXXXXXX</agent_hash>
    </logoutagent>
</request>

Respuesta:
En caso de agente que existe y está logoneado:
<response id="3">
    <logoutagent_response>
        <status>logged-out</status>
    </logoutagent_response>
</response>

En caso de que el agente no existe:
<response id="3">
    <logoutagent_response>
        <status>logged-out</status>
        <failure>
            <code>417</code>
            <message>Invalid agent number</message>
        </failure>
    </logoutagent_response>
</response>

Requerimiento "getcampaigninfo"

Este requerimiento recupera información estática sobre la campaña. Por 
información estática, se entiende la información que no cambia como consecuencia
del progreso de las llamadas pertenecientes a la campaña.

Atributos o argumentos:
* campaign_type: Estilo de la campaña. Uno de los valores 'incoming','outgoing'
* campaign_id: ID de la campaña. 

Respuesta:
* name: Nombre de la campaña
* type: Tipo de la campaña, uno de 'incoming' u 'outgoing'
* startdate: Fecha desde la cual tiene vigencia la campaña.
* enddate: Fecha hasta la cual tiene vigencia la campaña
* working_time_starttime: Hora desde la que se considera activa la campaña
* working_time_endtime: Hora hasta la que se considera activa la campaña
* queue: Cola usada para la campaña
* retries: (Sólo campañas salientes) Máximo número de reintentos para mismo número
* context: (Sólo campañas salientes) Contexto Asterisk desde el que se marca
* maxchan: (Sólo campañas salientes) Máximo número de llamadas a realizar
* trunk: (Opcional, sólo campañas salientes) Troncal a usar para las llamadas.
  Si no aparece, las llamadas se generan a través del plan de marcado de FreePBX
* status: Uno de los valores 'active', 'inactive', 'finished' (sólo campañas 
  salientes)
* script: Texto a usar como el guión de la campaña. NOTA: en la implementación
  actual, el texto almacenado se genera con un widget de edición que agrega
  estilos HTML/CSS en este campo. El cliente debe estar preparado para mostrar
  tales estilos, o de filtrarlos y mostrar únicamente el texto.
* forms: Elemento que contiene uno o más elementos 'form'
  * form: Elemento presente una o más veces. Representa un formulario que se puede
    usar para recoger información sobre las llamadas asociadas a la campaña. El
    elemento tiene los siguientes atributos:
    * id: ID de la base de datos para el formulario. 
    Además, el elemento contiene uno o más elementos:
    * field: Campo de entrada de información para este formulario. 
    Cada elemento field tiene los siguientes atributos:
    * order: Orden de presentación del campo
    * id: ID de base de datos del campo de entrada
    Además cada elemento field contiene a su vez los siguientes elementos:
    * label: Etiqueta asociada al campo
    * type: Uno de los siguientes: TEXT TEXTAREA DATE LABEL LIST
    * maxsize: (Opcional) Especifica la longitud máxima de la entrada, en caracteres
    * default_value: (Opcional) Un valor por omisión sugerido al cliente que
      muestra el formulario. ATENCION: este valor debe de ser guardado como 
      el valor elegido, como si hubiese sido ingresado por el agente.
    * options: (Opcional): Sólo presente cuando type es LIST. Contiene múltiples
      elementos 'value'.
      * value: Uno de los múltiples valores que se aceptan como entrada para 
        este campo.

Ejemplo:
<request id="6">
    <getcampaigninfo>
        <campaign_type>outgoing</campaign_type>
        <campaign_id>4</campaign_id>
    </getcampaigninfo>
</request>

Respuesta: 
Campaña saliente:
<response id="6">
    <getcampaigninfo_response>
        <name>Campa&#xF1;a de prueba</name>
        <type>outgoing</type>
        <startdate>2011-01-14</startdate>
        <enddate>2011-02-14</enddate>
        <working_time_starttime>00:00:00</working_time_starttime>
        <working_time_endtime>23:59:00</working_time_endtime>
        <queue>8000</queue>
        <retries>5</retries>
        <context>from-internal</context>
        <maxchan>23</maxchan>
        <status>inactive</status>
        <script>Prueba ...</script>
        <forms>
            <form id="1">
                <field order="1" id="1">
                    <label>CampoDeTexto</label>
                    <type>TEXT</type>
                    <maxsize>250</maxsize>
                </field>
                <field order="2" id="2">
                    <label>CampoDeLista</label>
                    <type>LIST</type>
                    <maxsize>250</maxsize>
                    <options>
                        <value>aaa</value>
                        <value>bbb</value>
                        <value>ccc</value>
                    </options>
                </field>
                <field order="3" id="3">
                    <label>CampoDeFecha</label>
                    <type>DATE</type>
                    <default_value>2011-01-01</default_value>
                </field>
                <field order="4" id="4">
                    <label>CampoDeTextarea</label>
                    <type>TEXTAREA</type>
                    <maxsize>250</maxsize>
                    <default_value>Un texto bien grande</default_value>
                </field>
                <field order="5" id="5">
                    <label>CampoDeEtiqueta</label>
                    <type>LABEL</type>
                </field>
            </form>
        </forms>
    </getcampaigninfo_response>
</response>

Campaña entrante:
<response id="7">
    <getcampaigninfo_response>
        <name>Prueba campania entrante</name>
        <type>incoming</type>
        <startdate>2011-01-20</startdate>
        <enddate>2011-02-01</enddate>
        <working_time_starttime>00:00:00</working_time_starttime>
        <working_time_endtime>23:59:59</working_time_endtime>
        <queue>8001</queue>
        <status>active</status>
        <script>Gatito lindo</script>
        <forms>
            <form id="1">
                <field order="1" id="1">
                    <label>CampoDeTexto</label>
                    <type>TEXT</type>
                    <maxsize>250</maxsize>
                </field>
                <field order="2" id="2">
                    <label>CampoDeLista</label>
                    <type>LIST</type>
                    <maxsize>250</maxsize>
                    <options>
                        <value>aaa</value>
                        <value>bbb</value>
                        <value>ccc</value>
                    </options>
                </field>
                <field order="3" id="3">
                    <label>CampoDeFecha</label>
                    <type>DATE</type>
                    <default_value>2011-01-01</default_value>
                </field>
                <field order="4" id="4">
                    <label>CampoDeTextarea</label>
                    <type>TEXTAREA</type>
                    <maxsize>250</maxsize>
                    <default_value>Un texto bien grande</default_value>
                </field>
                <field order="5" id="5">
                    <label>CampoDeEtiqueta</label>
                    <type>LABEL</type>
                </field>
            </form>
        </forms>
    </getcampaigninfo_response>
</response>

En caso de campaña no encontrada:
<response id="7">
    <getcampaigninfo_response>
        <failure>
            <code>404</code>
            <message>Campaign not found</message>
        </failure>
    </getcampaigninfo_response>
</response>

Requerimiento "getcampaignstatus"

Requerimiento "getcallinfo"

Este requerimiento recupera información sobre una llamada específica de una
campaña. Actualmente sólo se recupera información que se puede leer desde la
base de datos, y no información sobre la posible llamada en curso.

Atributos o argumentos:
* campaign_type: Tipo de la campaña, uno de 'incoming' u 'outgoing'
* campaign_id: ID en la base de datos de la campaña a la que pertenece la 
  llamada. Este valor es opcional para una llamada entrante, pero es requerido
  para una llamada saliente.
* call_id: ID en la base de datos de la llamada que se interroga

Respuesta:
* agent_number: Agente que ha sido enlazado con la llamada. Este identificador 
  está de la forma 'Agent/9000'.
* call_type: Uno de los dos valores 'incoming' u 'outgoing', para indicar si la
  llamada es entrante o saliente, respectivamente.
* campaign_id: ID de la base de datos de la campaña, un entero. Este valor es
  requerido para identificar una llamada saliente. Para una llamada entrante,
  este valor es opcional, e incluso puede estar omitido, si la llamada entrante
  no está asociada a una campaña.
* call_id: ID de la base de datos de la llamada, un entero. Con este ID, y el 
  valor de call_type (y campaign_id para llamadas salientes), el código cliente
  puede identificar la llamada a procesar para formularios.
* phone: El número que fue marcado, o el Caller-ID que fue recibido.
* status: El estado de la llamada. Usualmente es 'Success' para llamada saliente
  y 'activa' para llamada entrante.
* uniqueid: ID interno de Asterisk que identifica a la llamada remota.
* datetime_originate: (Sólo llamada saliente) Fecha y hora en que se inició el
  marcado de la llamada.
* datetime_originateresponse: (Sólo llamada saliente) Fecha y hora en que se
  recibió respuesta del marcado de la llamada.
* datetime_join: Fecha y hora en que la llamada ingresó a la cola.
* datetime_linkstart: Fecha y hora en que la llamada fue enlazada al agente.
* retries: (Opcional) Número de veces que la llamada ha sido reintentada.
* trunk: (Sólo llamada entrante) Troncal a través de la cual se recibió la llamada.
  Por ejemplo: DAHDI/4 o SIP/unatroncal
* queue: (Sólo llamada entrante) Cola que recibió la llamada
* call_attributes: (Opcional) Atributos asociados a la llamada en la base de 
  datos. Esta es una lista que contiene múltiples elementos 'attribute'. Cada
  elemento attribute tiene los siguientes elementos:
  * label: Etiqueta de texto asociada al atributo
  * value: Valor de texto del atributo
  * order: Orden en que se sugiere que se muestren los atributos
* matching_contacts: (Opcional, sólo llamada entrante) Lista de contactos cuyo
  número de teléfono coincide con el Caller-ID de la llamada. Si hay más de un
  contacto, la interfaz debe usar el requerimiento setcontact para indicar cuál
  contacto debe asociarse con la llamada recibida. Este elemento es una lista
  que contiene múltiples elementos 'contact'. Cada elemento contact tiene un
  atributo 'id' que debe usarse en el requerimiento setcontact. Además cada 
  contacto tiene múltiples elementos 'attribute'. Cada elemento attribute
  contiene los siguientes elementos:
  * label: Etiqueta de texto asociada al atributo del contacto
  * value: Valor de texto del atributo del contacto
  * order: Orden en que se sugiere que se muestren los atributos del contacto
* call_survey: (Opcional) Lista de formularios con los datos recogidos para esta
  llamada. Este elemento sólo está presente si se ha recogido previamente datos
  para esta llamada. Este elemento es una lista que contiene múltiples elementos
  'form'. Cada elemento form tiene un atributo 'id' que debe usarse en el 
  requerimiento saveformdata. Además cada form tiene múltiples elementos 
  'field'. Cada elemento field tiene un atributo 'id' que pueden usarse también
  en el requerimiento saveformdata. El elemento field tiene ademas los 
  siguientes elementos:
  * label: Etiqueta del elemento de formulario
  * value: Valor que fue recogido en el formulario

Ejemplo:
<request id="8">
    <getcallinfo>
        <campaign_type>outgoing</campaign_type>
        <campaign_id>5</campaign_id>
        <call_id>25</call_id>
    </getcallinfo>
</request>

Respuesta:
Llamada saliente:
<response id="8">
    <getcallinfo_response>
        <calltype>outgoing</calltype>
        <call_id>27</call_id>
        <campaign_id>5</campaign_id>
        <phone>1065</phone>
        <status>Success</status>
        <uniqueid>1296517619.3</uniqueid>
        <duration>187</duration>
        <datetime_originate>2011-01-31 18:46:59</datetime_originate>
        <datetime_originateresponse>2011-01-31 18:47:07</datetime_originateresponse>
        <datetime_join>2011-01-31 18:47:07</datetime_join>
        <datetime_linkstart>2011-01-31 18:47:07</datetime_linkstart>
        <datetime_linkend>2011-01-31 18:50:14</datetime_linkend>
        <retries>1</retries>
        <agent_number>Agent/9000</agent_number>
        <call_attributes>
            <attribute><label>Nombre</label><value>Zoila</value><order>1</order></attribute>
            <attribute><label>Apellido</label><value>Chacha</value><order>2</order></attribute>
            <attribute><label>Campo1</label><value>gato</value><order>3</order></attribute>
            <attribute><label>Campo2</label><value>perro</value><order>4</order></attribute>
            <attribute><label>Campo3</label><value>mono</value><order>5</order></attribute>
        </call_attributes>
        <call_survey>
            <form id="1">
                <field id="1">
                    <label>CampoDeTexto</label>
                    <value>Texto de campo de texto</value>
                </field>
                <field id="2">
                    <label>CampoDeLista</label>
                    <value>bbb</value>
                </field>
                <field id="3">
                    <label>CampoDeFecha</label>
                    <value>2011-02-01</value>
                </field>
                <field id="4">
                    <label>CampoDeTextarea</label>
                    <value>Texto de campo de textarea</value>
                </field>
            </form>
        </call_survey>
    </getcallinfo_response>
</response>

Llamada entrante:
<response id="8">
    <getcallinfo_response>
        <calltype>incoming</calltype>
        <call_id>8</call_id>
        <campaign_id>1</campaign_id>
        <phone>1065</phone>
        <status>terminada</status>
        <uniqueid>1296256837.24</uniqueid>
        <duration>205</duration>
        <datetime_join>2011-01-28 18:20:38</datetime_join>
        <datetime_linkstart>2011-01-28 18:20:38</datetime_linkstart>
        <datetime_linkend>2011-01-28 18:24:03</datetime_linkend>
        <trunk>SIP/1065</trunk>
        <queue>8001</queue>
        <agent_number>Agent/9000</agent_number>
        <call_attributes>
            <attribute><label>first_name</label><value>Usuario</value><order>1</order></attribute>
            <attribute><label>last_name</label><value>Windows</value><order>2</order></attribute>
            <attribute><label>phone</label><value>1065</value><order>3</order></attribute><attribute>
            <label>cedula_ruc</label><value>0915172176</value><order>4</order></attribute>
        </call_attributes>
        <matching_contacts>
            <contact id="2">
                <attribute><label>first_name</label><value>Usuario</value><order>1</order></attribute>
                <attribute><label>last_name</label><value>Windows</value><order>2</order></attribute>
                <attribute><label>phone</label><value>1065</value><order>3</order></attribute>
                <attribute><label>cedula_ruc</label><value>0915172176</value><order>4</order></attribute>
            </contact>
        </matching_contacts>
        <call_survey>
            <form id="1">
                <field id="1">
                    <label>CampoDeTexto</label>
                    <value>Texto de campo de texto</value>
                </field>
                <field id="2">
                    <label>CampoDeLista</label>
                    <value>bbb</value>
                </field>
                <field id="3">
                    <label>CampoDeFecha</label>
                    <value>2011-02-01</value>
                </field>
                <field id="4">
                    <label>CampoDeTextarea</label>
                    <value>Texto de campo de textarea</value>
                </field>
            </form>
        </call_survey>
    </getcallinfo_response>
</response>


Requerimiento "setcontact"

Este requerimiento ordena asignar el ID de contacto para la llamada entrante, en
el caso de que existan múltiples contactos cuyo número telefónico coincida con 
el Caller-ID anunciado para la llamada entrante. No es necesario que la llamada
esté activa al momento de asignar.

Atributos o argumentos:
* agent_number: Agente que está asignando el contacto. Este identificador está
  de la forma Agent/9000.
* agent_hash: Hash de autenticación para el agente.
* call_id: ID en la base de datos de la llamada que se asigna su contacto
* contact_id: ID en la base de datos del contacto a asociar con esta llamada.

Ejemplo:

<request id="10">
    <setcontact>
        <agent_number>Agent/9000</agent_number>
        <agent_hash>XXXXXXXXXXXXXXXXXXXXXXXXX</agent_hash>
        <call_id>8</call_id>
        <contact_id>3</contact_id>
    </setcontact>
</request>

Respuesta:
Para caso de éxito:
<response id="10">
    <setcontact_response>
        <success/>
    </setcontact_response>
</response>

Para ID de contacto no encontrado:
<response id="10">
    <setcontact_response>
        <failure>
            <code>404</code>
            <message>Contact ID not found</message>
        </failure>
    </setcontact_response>
</response>

Para ID de llamada no encontrada:
<response id="10">
    <setcontact_response>
        <failure>
            <code>404</code>
            <message>Call ID not found</message>
        </failure>
    </setcontact_response>
</response>

Requerimiento "saveformdata"

Este requerimiento ordena almacenar la información recogida de los formularios,
y la asocia a una llamada en particular. 

Atributos o argumentos:
* agent_number: Agente que manda a guardar los datos. Este identificador está 
  de la forma Agent/9000
* agent_hash: Hash de autenticación para el agente.
* campaign_type: Tipo de la campaña, uno de 'incoming' u 'outgoing'
* call_id: ID en la base de datos de la llamada que se almacena
* forms: Colección de múltiples elementos 'form':
  * form: Posee el siguiente atributo:
    * id: ID en la base de datos del formulario a usar. Este valor se puede
      obtener con las llamadas getcallinfo y getcampaigninfo.
    Contiene múltiples elementos 'field':
    * field: Posee el siguiente atributo:
      * id: ID en la base de datos del campo de formulario a usar. Este valor 
        se puede obtener con las llamadas getcallinfo y getcampaigninfo.
      Contiene como texto de la etiqueta el valor a almacenar en la base de datos.

Ejemplo:
<request id="9">
    <saveformdata>
        <agent_number>Agent/9000</agent_number>
        <agent_hash>XXXXXXXXXXXXXXXXXXXXXXXXX</agent_hash>
        <campaign_type>outgoing</campaign_type>
        <call_id>27</call_id>
        <forms>
            <form id="1">
                <field id="1">Texto de campo de texto</field>
                <field id="2">bbb</field>
                <field id="3">2011-02-01</field>
                <field id="4">Texto de campo de textarea</field>
                <field id="5">xxx</field>
            </form>
        </forms>
    </saveformdata>
</request>

Respuesta:
En caso de éxito:
<response id="9">
    <saveformdata_response>
        <success/>
    </saveformdata_response>
</response>

En caso de formulario no encontrado:
<response id="9">
    <saveformdata_response>
        <failure>
            <code>404</code>
            <message>Form ID not found: 0</message>
        </failure>
    </saveformdata_response>
</response>

En caso de campo no encontrado:
<response id="9">
    <saveformdata_response>
        <failure>
            <code>404</code>
            <message>Field ID not found in form: 1 - 0</message>
        </failure>
    </saveformdata_response>
</response>

En caso de valor de texto demasiado grande:
<response id="9">
    <saveformdata_response>
        <failure>
            <code>413</code>
            <message>Form value too large: 1 - 4</message>
        </failure>
    </saveformdata_response>
</response>

En caso de valor de campo LIST no permitido:
<response id="9">
    <saveformdata_response>
        <failure>
            <code>406</code>
            <message>Value not in list of accepted values: 1 - 2</message>
        </failure>
    </saveformdata_response>
</response>

En caso de formato de fecha inválido para campo DATE:
<response id="9">
    <saveformdata_response>
        <failure>
            <code>406</code>
            <message>Date format not acceptable, must be yyyy-mm-dd or yyyy-mm-dd hh:mm:ss: 1 - 3</message>
        </failure>
    </saveformdata_response>
</response>
Nota: para ayudar a la depuración, se adjunta el ID de formulario y el ID del campo que produjeron el error.

Requerimiento "hold"

Este requerimiento se usa para poner una llamada en hold. Internamente se manda
la llamada a una de las extensiones de parqueo. Al poner la llamada en hold, el
abonado remoto debería escuchar música en espera, y el agente escucha la música
de la cola, pero no recibirá más llamadas. 

Atributos o argumentos:
* agent_number: Agente que se va a pausar. Este identificador está de la forma 
  'Agent/9000'.
* agent_hash: Hash de autenticación para el agente.

Ejemplo:
<request id="11">
    <hold>
        <agent_number>Agent/9000</agent_number>
        <agent_hash>XXXXXXXXXXXXXXXXXXXXXXXXX</agent_hash>
    </hold>
</request>

Respuesta:
Para caso de éxito:
<response id="11">
    <hold_response>
        <success/>
    </hold_response>
</response>

Para agente no encontrado, o no logoneado vía ECCP:
<response id="11">
    <hold_response>
        <failure>
            <code>404</code>
            <message>Agent not found or not logged in through ECCP</message>
        </failure>
    </hold_response>
</response>

En caso de que FreePBX no haya activado el soporte de parqueo:
<response id="11">
    <hold_response>
        <failure>
            <code>500</code>
            <message>Parked call extension is disabled</message>
        </failure>
    </hold_response>
</response>

Para agente que está logoneado pero no está atendiendo una llamada: 
<response id="11">
    <hold_response>
        <failure>
            <code>417</code>
            <message>Agent currenty not handling a call</message>
        </failure>
    </hold_response>
</response>

Para error en Asterisk o en base de datos que impide completar:
<response id="11">
    <hold_response>
        <failure>
            <code>500</code>
            <message>Unable to start agent hold</message>
        </failure>
    </hold_response>
</response>

Requerimiento "unhold"
Este requerimiento se usa para recuperar la llamada que se haya mandado a hold,
y volver a hablar con el abonado.

Atributos o argumentos:
* agent_number: Agente que se va a pausar. Este identificador está de la forma 
  'Agent/9000'.
* agent_hash: Hash de autenticación para el agente.

Ejemplo:
<request id="11">
    <unhold>
        <agent_number>Agent/9000</agent_number>
        <agent_hash>XXXXXXXXXXXXXXXXXXXXXXXXX</agent_hash>
    </unhold>
</request>

Respuesta:
Para caso de éxito:
<response id="11">
    <unhold_response>
        <success/>
    </unhold_response>
</response>

Para agente no encontrado, o no logoneado vía ECCP:
<response id="11">
    <unhold_response>
        <failure>
            <code>404</code>
            <message>Agent not found or not logged in through ECCP</message>
        </failure>
    </unhold_response>
</response>

En caso de que FreePBX no haya activado el soporte de parqueo:
<response id="11">
    <unhold_response>
        <failure>
            <code>500</code>
            <message>Parked call extension is disabled</message>
        </failure>
    </unhold_response>
</response>

Para agente que ha recibido una llamada mientras estaba en hold: 
<response id="11">
    <unhold_response>
        <failure>
            <code>417</code>
            <message>Agent currenty handling a call</message>
        </failure>
    </unhold_response>
</response>

Para error en Asterisk o en base de datos que impide completar:
<response id="11">
    <unhold_response>
        <failure>
            <code>500</code>
            <message>Unable to stop agent hold</message>
        </failure>
    </unhold_response>
</response>

Requerimiento "transfercall"

Requerimiento "pauseagent"

Este requerimiento se usa para poner al agente en pausa debido a un break conocido.
El ID a usar para el break se obtiene con el requerimiento "getpauses". 

Atributos o argumentos:
* agent_number: Agente que se va a pausar. Este identificador está de la forma 
  'Agent/9000'.
* agent_hash: Hash de autenticación para el agente.
* pause_type: ID de base de datos de la pausa (break) a usar.

Ejemplo:
<request id="11">
    <pauseagent>
        <agent_number>Agent/9000</agent_number>
        <agent_hash>XXXXXXXXXXXXXXXXXXXXXXXXX</agent_hash>
        <pause_type>3</pause_type>
    </pauseagent>
</request>

Respuesta:
Para caso de éxito:
<response id="11">
    <pauseagent_response>
        <success/>
    </pauseagent_response>
</response>

Para agente que ya está en otro break:
<response id="11">
    <pauseagent_response>
        <failure>   
            <code>417</code>
            <message>Agent already in incompatible break</message>
        </failure>
    </pauseagent_response>
</response>

Para break inválido:
<response id="11">
    <pauseagent_response>
        <failure>
            <code>404</code>
            <message>Break ID not found or not active</message>
        </failure>
    </pauseagent_response>
</response>

Para agente no encontrado, o no logoneado vía ECCP:
<response id="11">
    <pauseagent_response>
        <failure>
            <code>404</code>
            <message>Agent not found or not logged in through ECCP</message>
        </failure>
    </pauseagent_response>
</response>

Requerimiento "unpauseagent"

Este requerimiento se usa para sacar al agente de la pausa en la cual se haya
puesto previamente con el requerimiento "pauseagent".

Atributos o argumentos:
* agent_number: Agente que se va a sacar de pausa. Este identificador está de la
  forma 'Agent/9000'.
* agent_hash: Hash de autenticación para el agente.

Ejemplo:
<request id="12">
    <unpauseagent>
        <agent_number>Agent/9000</agent_number>
        <agent_hash>XXXXXXXXXXXXXXXXXXXXXXXXX</agent_hash>
    </unpauseagent>
</request>

Respuesta:
Para caso de éxito:
<response id="12">
    <unpauseagent_response>
        <success/>
    </unpauseagent_response>
</response>

Requerimiento "getpauses"

Este requerimiento se usa para obtener el listado de las pausas (breaks) que
el agente puede usar para entrar en una pausa auditada.

Atributos o argumentos:
Este requerimiento no tiene atributos:

Respuesta:
* pause: Elemento múltiple. Contiene el siguiente atributo:
  * id: ID en la base de datos de la pausa
  Contiene las siguientes etiquetas:
  * name: Nombre corto de la pausa
  * status: Estado de la pausa. Puede valer 'A' para activa, 'I' para inactiva.
    Sólo deben usarse las pausas activas.
  * type: Tipo de pausa. Actualmente sólo se devuelven las pausas de tipo 'B'
    (break). La pausa de tipo 'H' (hold) está reservada para uso interno.
  * description: Descripción larga de la pausa.

Ejemplo:
<request id="10"><getpauses /></request>

Respuesta:
<response id="10">
    <getpauses_response>
        <pause id="2">
            <name>Ba&#xF1;o</name>
            <status>A</status>
            <type>B</type>
            <description>Para ir al ba&#xF1;o</description>
        </pause>
        <pause id="3">
            <name>Almuerzo</name>
            <status>A</status>
            <type>B</type>
            <description>Para salir a comer</description>
        </pause>
    </getpauses_response>
</response>

Requerimiento "hangup"

Este requerimiento manda a colgar la llamada en curso que está atendiendo el 
agente. Se requiere que el agente esté logoneado y tenga una llamada activa.


Atributos o argumentos:
* agent_number: Agente que se va a sacar de pausa. Este identificador está de la
  forma 'Agent/9000'.
* agent_hash: Hash de autenticación para el agente.

Ejemplo:
<request id="13">
    <hangup>
        <agent_number>Agent/9000</agent_number>
        <agent_hash>XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX</agent_hash>
    </hangup>
</request>

Respuesta:
Para caso de éxito:
<response id="13">
    <hangup_response>
        <success/>
    </hangup_response>
</response>

Para agente inválido:
<response id="13">
    <hangup_response>
        <failure>
            <code>404</code>
            <message>Specified agent not found</message>
        </failure>
    </hangup_response>
</response>

Para agente que no está atendiendo llamada, o que no está logoneado:
<response id="13">
    <hangup_response>
        <failure>
            <code>417</code>
            <message>Agent not in call</message>
        </failure>
    </hangup_response>
</response>

Para error Asterisk al momento de colgar la llamada:
<response id="13">
    <hangup_response>
        <failure>
            <code>500</code>
            <message>Cannot hangup agent call</message>
        </failure>
    </hangup_response>
</response>
